package gepetto.util;

public class LNodeList<T> {
	private int size;

	private LNode<T> head;

	private LNode<T> tail;

	private LNode<T> current;

	public LNodeList() {
		this (Integer.MAX_VALUE);
	}
	
	public LNodeList (int limit) {
		// TODO: Limit support
		head = null;
		tail = null;
		current = null;
		size = 0;
	}

	public T getHead() {
		if (head != null) {
			return head.data;
		}

		return null;
	}

	public T getTail() {
		if (tail != null) {
			return tail.data;
		}

		return null;
	}

	public T popHead() {
		T t = null;
		if (head != null) {
			--size;
			t = head.data;
			if (current == head) {
				current = head.next;
			}
			head = head.next;
			if (head == null) {
				tail = null;
			}
		}

		return t;
	}

	public T popTail() {
		T t = null;
		if (tail != null) {
			--size;
			t = tail.data;
			if (current == tail) {
				current = tail.previous;
			}
			tail = tail.previous;
			if (tail == null) {
				head = null;
			}
		}
		return t;
	}

	public void pushHead(T t) {
		LNode<T> node = new LNode<T>(t);
		++size;
		if (head != null) {
			head.previous = node;
			node.next = head;
		}
		head = node;

		if (current == null) {
			current = head;
		}
		if (tail == null) {
			tail = head;
		}
		if (tail.previous == null) {
			tail.previous = head;
		}
	}

	public void pushTail(T t) {
		LNode<T> node = new LNode<T>(t);
		++size;
		if (tail != null) {
			tail.next = node;
			node.previous = tail;
		}
		tail = node;

		if (current == null) {
			current = tail;
		}
		if (head == null) {
			head = tail;
		} else if (head.next == null) {
			head.next = tail;
		}
	}

	public T getCurrent() {
		if (current != null) {
			return current.data;
		}
		return null;
	}

	public void currentNext() {
		if ((current != null) && (current.next != null)) {
			current = current.next;
		}
	}

	public void currentPrevious() {
		if ((current != null) && (current.previous != null)) {
			current = current.previous;
		}
	}

	public boolean isEmpty() {
		return (current == null) && (head == null) && (tail == null);
	}

	public int size() {
		return size;
	}

	public void currentToHead() {
		current = head;
	}

	public void currentToTail() {
		current = tail;
	}

	public void clear() {
		tail = head = current = null;
		size = 0;
	}

	public void clearToTail() {
		if (tail != null) {
			while (tail != current) {
				this.popTail();
			}
		}
	}

	public void clearToHead() {
		if (head != null) {
			while (head != current) {
				this.popHead();
			}
		}
	}
	
	public T popCurrent () {
		T t = null;
		if (current != null) {
			--size;
			t = current.data;
			if (current.next != null) {
				if (current.previous != null) {
					current.previous.next = current.next;
					current.next.previous = current.previous;
					current = current.next; // Convenio: current es por defecto el siguiente
				}
				else {
					current.next.previous = null;
					current = current.next;
					head = current;
				}
			}
			else if (current.previous != null) {
				current.previous.next = null;
				current = current.previous;
				tail = current;
			}
			else {
				head = current = tail = null;
			}
		}
		return t;
	}

	public boolean hasPrevious () {
		return current != head;
	}

	public boolean hasNext () {
		return current != tail;
	}
}
